﻿/****************************** Module Header ******************************\
 * Module Name:  mxTreeView.cs
 * Module Version:  1.0.0 (2012-8-16)
 * Copyright (c) AnyEB.Com (14507247@qq.com)
 * 
 * This class is used for mxTreeView. 
 * 
 * This source is subject to the GPL License.
 * See http://www.gnu.org/licenses/gpl.html.
 * All other rights reserved.
 * 
 * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
 * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Drawing;
using System.Windows.Forms;

namespace UCL.Forms
{
    public static class mxTreeView
    {
        /// <summary>
        /// 从XML加载节点
        /// </summary>
        /// <param name="tn"></param>
        /// <param name="xe"></param>
        /// <param name="nodetag"></param>
        /// <param name="idtag"></param>
        /// <param name="nametag"></param>
        public static void AddNodesByXElement(this TreeNode tn, XElement xe, string nodetag = "node", string idtag = "ID", string nametag = "Name")
        {
            AddNodesByXElement_Func(xe, tn, nodetag, nametag, idtag);
        }

        /// <summary>
        /// AddNodesByXElement私有遍历方法
        /// </summary>
        /// <param name="xe"></param>
        /// <param name="tn"></param>
        /// <param name="nodetag"></param>
        /// <param name="nametag"></param>
        /// <param name="idtag"></param>
        private static void AddNodesByXElement_Func(XElement xe, TreeNode tn, string nodetag, string nametag, string idtag)
        {
            if (!xe.HasElements) return;
            TreeNode tn1;
            foreach (XElement xe1 in xe.Elements(nodetag))
            {
                tn1 = new TreeNode(xe1.Attribute(nametag).GetValue()) { Name = xe1.Attribute(idtag).GetValue() };
                if (xe1.HasElements) AddNodesByXElement_Func(xe1, tn1, nodetag, nametag, idtag);
                tn.Nodes.Add(tn1);
            }
        }

        /// <summary>
        /// 加载整个xml文档
        /// </summary>
        /// <param name="tv"></param>
        /// <param name="xmlstring"></param>
        /// <param name="roottext"></param>
        /// <param name="rootvalue"></param>
        /// <param name="nodename"></param>
        /// <param name="idattr"></param>
        /// <param name="nameattr"></param>
        /// <remarks>2013-9-6 by sutroon</remarks>
        public static void LoadXmlString(this TreeView tv, string xmlstring, string roottext = "所有", string rootvalue = "0", string nodename = "node", string idattr = "ID", string nameattr = "Name")
        {
            XElement doc = XElement.Parse(xmlstring);
            TreeNode tn = new TreeNode(roottext) { Name = rootvalue };
            tn.AddNodesByXElement(doc);
            tv.Nodes.Add(tn);
        }

        /// <summary>
        /// 添加节点
        /// </summary>
        /// <param name="tv"></param>
        /// <param name="xe"></param>
        /// <param name="rootNode"></param>
        /// <param name="expandall"></param>
        public static void AddNodesByXElement(this TreeView tv, XElement xe, TreeNode rootNode, bool expandall = true)
        {
            rootNode.AddNodesByXElement(xe);
            tv.Nodes.Clear();
            tv.Nodes.Add(rootNode);
            if (expandall) tv.ExpandAll();
        }

        /// <summary>
        /// 检测本节点是否是倒数第二级
        /// <para>2013-9-9 by sutroon</para>
        /// </summary>
        /// <param name="tn"></param>
        /// <returns></returns>
        public static bool IsPenultimateLevel(this TreeNode tn)
        {
            return tn != null && tn.Nodes.Count > 0 && tn.GetNodeCount(true) == tn.Nodes.Count;
        }

        /// <summary>
        /// 设置某节点的所有级联父子节点的勾选状态
        /// </summary>
        /// <param name="tv"></param>
        /// <remarks>
        /// <para>2013-9-6 by sutroon</para>
        /// <para>2014-5-15 by sutroon 重构</para>
        /// </remarks>
        public static void OnCheckedCheckingTreeNodes(this TreeView tv)
        {
            tv.NodeMouseClick += (object sender, TreeNodeMouseClickEventArgs e) =>
            {
                if (e.Button == MouseButtons.Left)
                {
                    if (e.Node.Checked) SetParentNodesCheckState(e.Node.Parent);
                    if (e.Node.IsPenultimateLevel()) SetChildNodesCheckState(e.Node, e.Node.Checked); // 倒数第二级才勾选所有子节点，不然编辑的时候很乱
                }
            };
        }
        /// <summary>
        /// 设置某节点的所有子节点的勾选状态
        /// </summary>
        /// <param name="tn"></param>
        /// <param name="state"></param>
        public static void SetChildNodesCheckState(this TreeNode tn, bool state)
        {
            foreach (TreeNode n in tn.Nodes)
            {
                n.Checked = state;
                if (n.Nodes.Count > 0) SetChildNodesCheckState(n, state);
            }
        }
        /// <summary>
        /// 设置某节点的所有父节点的勾选状态
        /// </summary>
        /// <param name="tn"></param>
        public static void SetParentNodesCheckState(this TreeNode tn)
        {
            if (tn == null) return;
            if (!tn.Checked) tn.Checked = true;
            SetParentNodesCheckState(tn.Parent);
        }

        /// <summary>
        /// 设置包含指定值的节点的勾选状态
        /// <para>如:tvRules.SetCheckBoxState(true, dt.GetValue&lt;string&gt;("PermissionRule").Split(','));</para>
        /// </summary>
        /// <param name="tv"></param>
        /// <param name="state"></param>
        /// <param name="values"></param>
        /// <remarks>2015-1-29 by sutroon</remarks>
        public static void SetCheckBoxState(this TreeView tv, bool state, string[] values)
        {
            tv.SetCheckBoxState(state, a => values.Contains(a.Name));
        }

        /// <summary>
        /// 设置包含指定值的节点的勾选状态
        /// <para>如：tv.SetCheckBoxState(true, a => new string[] { "a", "b" }.Contains(a.Name));</para>
        /// </summary>
        /// <param name="tv"></param>
        /// <param name="state"></param>
        /// <param name="values"></param>
        /// <remarks>
        /// <para>2013-9-9 by sutroon created</para>
        /// <para>2014-5-16 by sutroon 重构</para>
        /// </remarks>
        public static void SetCheckBoxState(this TreeView tv, bool state, Func<TreeNode, bool> func)
        {
            foreach (TreeNode tn in tv.Nodes)
            {
                if (func(tn)) tn.Checked = state;
                SetCheckBoxState_Func(tn, state, func);
            }
        }
        // SetCheckBoxState的私有函数
        private static void SetCheckBoxState_Func(TreeNode tn, bool state, Func<TreeNode, bool> func)
        {
            foreach (TreeNode n in tn.Nodes)
            {
                if (func(n)) n.Checked = state;
                if (n.Nodes.Count > 0) SetCheckBoxState_Func(n, state, func);
            }
        }

        /// <summary>
        /// 获取所有选中的值的列表
        /// </summary>
        /// <param name="tv"></param>
        /// <returns></returns>
        /// <remarks>2015-1-29 by sutroon</remarks>
        public static IList<string> GetCheckedValues(this TreeView tv)
        {
            return GetCheckedValues(tv, a => a.Name);
        }

        /// <summary>
        /// 获取所有选中的值的列表
        /// <para>如：tv.GetCheckedValues(a => a.Name);</para>
        /// </summary>
        /// <param name="tv"></param>
        /// <returns></returns>
        /// <remarks>2013-9-9 by sutroon</remarks>
        public static IList<string> GetCheckedValues(this TreeView tv, Func<TreeNode, string> func)
        {
            IList<string> lst = new List<string>();
            if (null == tv || tv.Nodes.Count == 0) return lst;
            foreach (TreeNode tn in tv.Nodes)
            {
                if (tn.Checked) lst.Add(func(tn));
                GetCheckedValues_Func(tn, func, ref lst);
            }
            return lst;
        }
        // GetCheckedValues的私有函数
        private static void GetCheckedValues_Func(TreeNode tn, Func<TreeNode, string> func, ref IList<string> lst)
        {
            foreach (TreeNode n in tn.Nodes)
            {
                if (n.Checked) lst.Add(func(n));
                if (n.Nodes.Count > 0) GetCheckedValues_Func(n, func, ref lst);
            }
        }
        /// <summary>
        /// 获取选中的数量
        /// </summary>
        /// <param name="tv"></param>
        /// <returns></returns>
        /// <remarks>2014-3-19 by sutroon</remarks>
        public static int GetCheckedItemsCount(this TreeView tv)
        {
            int i = 0;
            if (tv == null || tv.Nodes.Count == 0) return i;
            foreach (TreeNode n in tv.Nodes)
            {
                if (n.Checked) i++;
                GetCheckedItemsCount_Func(n, ref i);
            }
            return i;
        }
        private static void GetCheckedItemsCount_Func(TreeNode tn, ref int i)
        {
            foreach (TreeNode n in tn.Nodes)
            {
                if (n.Checked) i++;
                if (n.Nodes.Count > 0) GetCheckedItemsCount_Func(n, ref i);
            }
        }

        /// <summary>
        /// 获取当前选中的值
        /// <para>Since 2013-9-9 by sutroon</para>
        /// <para>如：tv.GetCurrentCheckedValue(a => a.Name);</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tv"></param>
        /// <param name="func"></param>
        /// <param name="defv">默认值</param>
        /// <param name="needChecked">是否需要勾选</param>
        /// <returns></returns>
        public static T GetCurrentCheckedValue<T>(this TreeView tv, Func<TreeNode, T> func, T defv, bool needChecked = false)
        {
            if (tv.SelectedNode == null) return defv;
            if (needChecked)
            {
                if (tv.SelectedNode.Checked) return func(tv.SelectedNode);
            }
            else
            {
                return func(tv.SelectedNode);
            }
            return defv;
        }

        /// <summary>
        /// 部门列表鼠标右键点击事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public static void OnRightMouseDownSelectedNode(this TreeView tv)
        {
            tv.MouseDown += (object sender, MouseEventArgs e) => { if (e.Button == MouseButtons.Right) tv.SelectedNode = tv.GetNodeAt(e.Location); };
        }

        /// <summary>
        /// 使节点在失去焦点后仍保持选中状态
        /// </summary>
        /// <param name="tv"></param>
        public static void KeepSelection(this TreeView tv)
        {
            tv.HideSelection = false;
            tv.DrawMode = TreeViewDrawMode.OwnerDrawText;
            tv.DrawNode += (object s, DrawTreeNodeEventArgs e) =>
            {
                e.Graphics.FillRectangle(Brushes.White, e.Node.Bounds);
                if (e.State == TreeNodeStates.Selected)
                {
                    e.Graphics.FillRectangle(Brushes.DodgerBlue, e.Bounds);
                    e.Graphics.DrawString(e.Node.Text, tv.Font, Brushes.White, new Rectangle(e.Node.Bounds.Left - 1, e.Node.Bounds.Top, e.Node.Bounds.Width + 2, e.Node.Bounds.Height));
                }
                else
                {
                    e.DrawDefault = true;
                }
            };
        }

        /// <summary>
        /// 选择节点
        /// <para>2012-11-28</para>
        /// </summary>
        /// <param name="tv"></param>
        /// <param name="v"></param>
        public static void SelectNodeByName(this TreeView tv, string v)
        {
            TreeNode[] arr = tv.Nodes.Find(v, true);
            if (arr.Length > 0) tv.SelectedNode = arr[0];
        }

        /// <summary>
        /// 收起指定深度的子节点
        /// <para>2012-11-28</para>
        /// </summary>
        /// <param name="tv"></param>
        /// <param name="level"></param>
        public static void CollapseByLevel(this TreeView tv, int level)
        {
            CollapseByLevelFunc(tv.Nodes, level);
        }

        private static void CollapseByLevelFunc(TreeNodeCollection nodes, int level)
        {
            foreach (TreeNode n in nodes)
            {
                if (n.Level == level) n.Collapse();
                if (n.Nodes.Count > 0 && n.Level < level) CollapseByLevelFunc(n.Nodes, level);
            }
        }
    }
}
